//: net/mindview/util/Countries.java
// "Flyweight" Maps and Lists of sample data.
package io.github.dunwu.javacore.container.bean;

import java.util.*;

public class Countries {

    public static final String[][] DATA = {
        // Africa
        { "ALGERIA", "Algiers" }, { "ANGOLA", "Luanda" }, { "BENIN", "Porto-Novo" }, { "BOTSWANA", "Gaberone" },
        { "BULGARIA", "Sofia" }, { "BURKINA FASO", "Ouagadougou" }, { "BURUNDI", "Bujumbura" },
        { "CAMEROON", "Yaounde" }, { "CAPE VERDE", "Praia" }, { "CENTRAL AFRICAN REPUBLIC", "Bangui" },
        { "CHAD", "N'djamena" }, { "COMOROS", "Moroni" }, { "CONGO", "Brazzaville" }, { "DJIBOUTI", "Dijibouti" },
        { "EGYPT", "Cairo" }, { "EQUATORIAL GUINEA", "Malabo" }, { "ERITREA", "Asmara" },
        { "ETHIOPIA", "Addis Ababa" }, { "GABON", "Libreville" }, { "THE GAMBIA", "Banjul" }, { "GHANA", "Accra" },
        { "GUINEA", "Conakry" }, { "BISSAU", "Bissau" }, { "COTE D'IVOIR (IVORY COAST)", "Yamoussoukro" },
        { "KENYA", "Nairobi" }, { "LESOTHO", "Maseru" }, { "LIBERIA", "Monrovia" }, { "LIBYA", "Tripoli" },
        { "MADAGASCAR", "Antananarivo" }, { "MALAWI", "Lilongwe" }, { "MALI", "Bamako" },
        { "MAURITANIA", "Nouakchott" }, { "MAURITIUS", "Port Louis" }, { "MOROCCO", "Rabat" },
        { "MOZAMBIQUE", "Maputo" }, { "NAMIBIA", "Windhoek" }, { "NIGER", "Niamey" }, { "NIGERIA", "Abuja" },
        { "RWANDA", "Kigali" }, { "SAO TOME E PRINCIPE", "Sao Tome" }, { "SENEGAL", "Dakar" },
        { "SEYCHELLES", "Victoria" }, { "SIERRA LEONE", "Freetown" }, { "SOMALIA", "Mogadishu" },
        { "SOUTH AFRICA", "Pretoria/Cape Town" }, { "SUDAN", "Khartoum" }, { "SWAZILAND", "Mbabane" },
        { "TANZANIA", "Dodoma" }, { "TOGO", "Lome" }, { "TUNISIA", "Tunis" }, { "UGANDA", "Kampala" },
        { "DEMOCRATIC REPUBLIC OF THE CONGO (ZAIRE)", "Kinshasa" }, { "ZAMBIA", "Lusaka" },
        { "ZIMBABWE", "Harare" },
        // Asia
        { "AFGHANISTAN", "Kabul" }, { "BAHRAIN", "Manama" }, { "BANGLADESH", "Dhaka" }, { "BHUTAN", "Thimphu" },
        { "BRUNEI", "Bandar Seri Begawan" }, { "CAMBODIA", "Phnom Penh" }, { "CHINA", "Beijing" },
        { "CYPRUS", "Nicosia" }, { "INDIA", "New Delhi" }, { "INDONESIA", "Jakarta" }, { "IRAN", "Tehran" },
        { "IRAQ", "Baghdad" }, { "ISRAEL", "Jerusalem" }, { "JAPAN", "Tokyo" }, { "JORDAN", "Amman" },
        { "KUWAIT", "Kuwait City" }, { "LAOS", "Vientiane" }, { "LEBANON", "Beirut" },
        { "MALAYSIA", "Kuala Lumpur" }, { "THE MALDIVES", "Male" }, { "MONGOLIA", "Ulan Bator" },
        { "MYANMAR (BURMA)", "Rangoon" }, { "NEPAL", "Katmandu" }, { "NORTH KOREA", "P'yongyang" },
        { "OMAN", "Muscat" }, { "PAKISTAN", "Islamabad" }, { "PHILIPPINES", "Manila" }, { "QATAR", "Doha" },
        { "SAUDI ARABIA", "Riyadh" }, { "SINGAPORE", "Singapore" }, { "SOUTH KOREA", "Seoul" },
        { "SRI LANKA", "Colombo" }, { "SYRIA", "Damascus" }, { "TAIWAN (REPUBLIC OF CHINA)", "Taipei" },
        { "THAILAND", "Bangkok" }, { "TURKEY", "Ankara" }, { "UNITED ARAB EMIRATES", "Abu Dhabi" },
        { "VIETNAM", "Hanoi" }, { "YEMEN", "Sana'a" },
        // Australia and Oceania
        { "AUSTRALIA", "Canberra" }, { "FIJI", "Suva" }, { "KIRIBATI", "Bairiki" },
        { "MARSHALL ISLANDS", "Dalap-Uliga-Darrit" }, { "MICRONESIA", "Palikir" }, { "NAURU", "Yaren" },
        { "NEW ZEALAND", "Wellington" }, { "PALAU", "Koror" }, { "PAPUA NEW GUINEA", "Port Moresby" },
        { "SOLOMON ISLANDS", "Honaira" }, { "TONGA", "Nuku'alofa" }, { "TUVALU", "Fongafale" },
        { "VANUATU", "< Port-Vila" }, { "WESTERN SAMOA", "Apia" },
        // Eastern Europe and former USSR
        { "ARMENIA", "Yerevan" }, { "AZERBAIJAN", "Baku" }, { "BELARUS (BYELORUSSIA)", "Minsk" },
        { "GEORGIA", "Tbilisi" }, { "KAZAKSTAN", "Almaty" }, { "KYRGYZSTAN", "Alma-Ata" },
        { "MOLDOVA", "Chisinau" }, { "RUSSIA", "Moscow" }, { "TAJIKISTAN", "Dushanbe" },
        { "TURKMENISTAN", "Ashkabad" }, { "UKRAINE", "Kyiv" }, { "UZBEKISTAN", "Tashkent" },
        // Europe
        { "ALBANIA", "Tirana" }, { "ANDORRA", "Andorra la Vella" }, { "AUSTRIA", "Vienna" },
        { "BELGIUM", "Brussels" }, { "BOSNIA", "-" }, { "HERZEGOVINA", "Sarajevo" }, { "CROATIA", "Zagreb" },
        { "CZECH REPUBLIC", "Prague" }, { "DENMARK", "Copenhagen" }, { "ESTONIA", "Tallinn" },
        { "FINLAND", "Helsinki" }, { "FRANCE", "Paris" }, { "GERMANY", "Berlin" }, { "GREECE", "Athens" },
        { "HUNGARY", "Budapest" }, { "ICELAND", "Reykjavik" }, { "IRELAND", "Dublin" }, { "ITALY", "Rome" },
        { "LATVIA", "Riga" }, { "LIECHTENSTEIN", "Vaduz" }, { "LITHUANIA", "Vilnius" },
        { "LUXEMBOURG", "Luxembourg" }, { "MACEDONIA", "Skopje" }, { "MALTA", "Valletta" }, { "MONACO", "Monaco" },
        { "MONTENEGRO", "Podgorica" }, { "THE NETHERLANDS", "Amsterdam" }, { "NORWAY", "Oslo" },
        { "POLAND", "Warsaw" }, { "PORTUGAL", "Lisbon" }, { "ROMANIA", "Bucharest" },
        { "SAN MARINO", "San Marino" }, { "SERBIA", "Belgrade" }, { "SLOVAKIA", "Bratislava" },
        { "SLOVENIA", "Ljuijana" }, { "SPAIN", "Madrid" }, { "SWEDEN", "Stockholm" }, { "SWITZERLAND", "Berne" },
        { "UNITED KINGDOM", "London" }, { "VATICAN CITY", "---" },
        // North and Central America
        { "ANTIGUA AND BARBUDA", "Saint John's" }, { "BAHAMAS", "Nassau" }, { "BARBADOS", "Bridgetown" },
        { "BELIZE", "Belmopan" }, { "CANADA", "Ottawa" }, { "COSTA RICA", "San Jose" }, { "CUBA", "Havana" },
        { "DOMINICA", "Roseau" }, { "DOMINICAN REPUBLIC", "Santo Domingo" }, { "EL SALVADOR", "San Salvador" },
        { "GRENADA", "Saint George's" }, { "GUATEMALA", "Guatemala City" }, { "HAITI", "Port-au-Prince" },
        { "HONDURAS", "Tegucigalpa" }, { "JAMAICA", "Kingston" }, { "MEXICO", "Mexico City" },
        { "NICARAGUA", "Managua" }, { "PANAMA", "Panama City" }, { "ST. KITTS", "-" }, { "NEVIS", "Basseterre" },
        { "ST. LUCIA", "Castries" }, { "ST. VINCENT AND THE GRENADINES", "Kingstown" },
        { "UNITED STATES OF AMERICA", "Washington, D.C." },
        // South America
        { "ARGENTINA", "Buenos Aires" }, { "BOLIVIA", "Sucre (legal)/La Paz(administrative)" },
        { "BRAZIL", "Brasilia" }, { "CHILE", "Santiago" }, { "COLOMBIA", "Bogota" }, { "ECUADOR", "Quito" },
        { "GUYANA", "Georgetown" }, { "PARAGUAY", "Asuncion" }, { "PERU", "Lima" }, { "SURINAME", "Paramaribo" },
        { "TRINIDAD AND TOBAGO", "Port of Spain" }, { "URUGUAY", "Montevideo" }, { "VENEZUELA", "Caracas" }, };

    static Map<String, String> map = new FlyweightMap();

    static List<String> names = new ArrayList<String>(map.keySet());

    public static void main(String[] args) {
        System.out.println(capitals(10));
        System.out.println(names(10));
        System.out.println(new HashMap<String, String>(capitals(3)));
        System.out.println(new LinkedHashMap<String, String>(capitals(3)));
        System.out.println(new TreeMap<String, String>(capitals(3)));
        System.out.println(new Hashtable<String, String>(capitals(3)));
        System.out.println(new HashSet<String>(names(6)));
        System.out.println(new LinkedHashSet<String>(names(6)));
        System.out.println(new TreeSet<String>(names(6)));
        System.out.println(new ArrayList<String>(names(6)));
        System.out.println(new LinkedList<String>(names(6)));
        System.out.println(capitals().get("BRAZIL"));
    }

    public static Map<String, String> capitals(int size) {
        return select(size); // A partial map
    }

    // Create a partial map of 'size' countries:
    static Map<String, String> select(final int size) {
        return new FlyweightMap() {
            @Override
            public Set<Map.Entry<String, String>> entrySet() {
                return new EntrySet(size);
            }
        };
    }

    // A partial list:
    public static List<String> names(int size) {
        return new ArrayList<String>(select(size).keySet());
    }

    public static Map<String, String> capitals() {
        return map; // The entire map
    }

    // All the names:
    public static List<String> names() {
        return names;
    }

    // Use AbstractMap by implementing entrySet()
    private static class FlyweightMap extends AbstractMap<String, String> {

        private static Set<Map.Entry<String, String>> entries = new EntrySet(DATA.length);

        @Override
        public Set<Map.Entry<String, String>> entrySet() {
            return entries;
        }

        private static class Entry implements Map.Entry<String, String> {

            int index;

            Entry(int index) {
                this.index = index;
            }

            @Override
            public String getKey() {
                return DATA[index][0];
            }

            @Override
            public String getValue() {
                return DATA[index][1];
            }

            @Override
            public String setValue(String value) {
                throw new UnsupportedOperationException();
            }

            @Override
            public int hashCode() {
                return DATA[index][0].hashCode();
            }

            @Override
            public boolean equals(Object o) {
                return DATA[index][0].equals(o);
            }

        }

        // Use AbstractSet by implementing size() & iterator()
        static class EntrySet extends AbstractSet<Map.Entry<String, String>> {

            private int size;

            EntrySet(int size) {
                if (size < 0) {
                    this.size = 0;
                }
                // Can't be any bigger than the array:
                else if (size > DATA.length) {
                    this.size = DATA.length;
                } else {
                    this.size = size;
                }
            }

            @Override
            public Iterator<Map.Entry<String, String>> iterator() {
                return new Iter();
            }

            @Override
            public int size() {
                return size;
            }

            private class Iter implements Iterator<Map.Entry<String, String>> {

                // Only one Entry object per Iterator:
                private Entry entry = new Entry(-1);

                @Override
                public boolean hasNext() {
                    return entry.index < size - 1;
                }

                @Override
                public Map.Entry<String, String> next() {
                    entry.index++;
                    return entry;
                }

                @Override
                public void remove() {
                    throw new UnsupportedOperationException();
                }

            }

        }

    }

} /*
 * Output: {ALGERIA=Algiers, ANGOLA=Luanda, BENIN=Porto-Novo, BOTSWANA=Gaberone,
 * BULGARIA=Sofia, BURKINA FASO=Ouagadougou, BURUNDI=Bujumbura, CAMEROON=Yaounde, CAPE
 * VERDE=Praia, CENTRAL AFRICAN REPUBLIC=Bangui} [ALGERIA, ANGOLA, BENIN, BOTSWANA,
 * BULGARIA, BURKINA FASO, BURUNDI, CAMEROON, CAPE VERDE, CENTRAL AFRICAN REPUBLIC]
 * {BENIN=Porto-Novo, ANGOLA=Luanda, ALGERIA=Algiers} {ALGERIA=Algiers, ANGOLA=Luanda,
 * BENIN=Porto-Novo} {ALGERIA=Algiers, ANGOLA=Luanda, BENIN=Porto-Novo}
 * {ALGERIA=Algiers, ANGOLA=Luanda, BENIN=Porto-Novo} [BULGARIA, BURKINA FASO,
 * BOTSWANA, BENIN, ANGOLA, ALGERIA] [ALGERIA, ANGOLA, BENIN, BOTSWANA, BULGARIA,
 * BURKINA FASO] [ALGERIA, ANGOLA, BENIN, BOTSWANA, BULGARIA, BURKINA FASO] [ALGERIA,
 * ANGOLA, BENIN, BOTSWANA, BULGARIA, BURKINA FASO] [ALGERIA, ANGOLA, BENIN, BOTSWANA,
 * BULGARIA, BURKINA FASO] Brasilia
 */// :~
